﻿// =====================================================
// Copyright (C) 2017-2018 Fanjia
// All rights reserved
// CLR版 本: 4.0.30319.42000
// 创建时间: 2018/1/2 18:09:52
// 创 建 人: 李伟
// ======================================================

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WindowsFormsApplication1.Common
{
    /// <summary>
    /// //读CSV文件类,读取指定的CSV文件，可以导出DataTable..........add by chujianqin
    /// </summary>
    public class CsvStreamReader
    {
        private ArrayList rowAL; //行链表,CSV文件的每一行就是一个链
        private string fileName; //文件名
        public DataTable csvDT = new DataTable();
        private Encoding encoding; //编码
        public DataTable table = new DataTable();
        bool IsFirst = true;
        List<ArrayList> arry = new List<ArrayList>();
        public CsvStreamReader()
        {
            this.rowAL = new ArrayList();
            this.fileName = "";
            this.encoding = Encoding.Default;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="fileName">文件名,包括文件路径</param>
        public CsvStreamReader(string fileName)
        {
            this.rowAL = new ArrayList();
            this.fileName = fileName;
            this.encoding = Encoding.Default;
            LoadCsvFile();
            DataView dataView = csvDT.DefaultView;
            dataView.Sort = "ID desc";
            csvDT = dataView.ToTable();
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="fileName">文件名,包括文件路径</param>
        /// <param name="encoding">文件编码</param>
        public CsvStreamReader(string fileName, Encoding encoding)
        {
            this.rowAL = new ArrayList();
            this.fileName = fileName;
            this.encoding = encoding;
            LoadCsvFile();
        }

        /// <summary>
        /// 载入CSV文件
        /// </summary>
        private void LoadCsvFile()
        {
            //对数据的有效性进行验证

            if (this.fileName == null)
            {
                throw new Exception("请指定要载入的CSV文件名");
            }
            else if (!File.Exists(this.fileName))
            {
                throw new Exception("指定的CSV文件不存在");
            }
            else
            {
            }
            if (this.encoding == null)
            {
                this.encoding = Encoding.Default;
            }
            DataTable dt = new DataTable();
            StreamReader sr = new StreamReader(this.fileName, this.encoding);
            string csvDataLine;

            csvDataLine = "";
            while (true)
            {
                string fileDataLine;

                fileDataLine = sr.ReadLine();
                if (fileDataLine == null)
                {
                    break;
                }
                if (csvDataLine == "")
                {
                    csvDataLine = fileDataLine;//GetDeleteQuotaDataLine(fileDataLine);
                }
                else
                {
                    csvDataLine += "\r\n" + fileDataLine;//GetDeleteQuotaDataLine(fileDataLine);
                }
                //如果包含偶数个引号，说明该行数据中出现回车符或包含逗号
                if (!IfOddQuota(csvDataLine))
                {
                    AddNewDataLine(csvDataLine);

                    csvDataLine = "";
                }
            }
            sr.Close();
            //数据行出现奇数个引号
            if (csvDataLine.Length > 0)
            {
                throw new Exception("CSV文件的格式有错误");
            }
        }

        /// <summary>
        /// 获取两个连续引号变成单个引号的数据行
        /// </summary>
        /// <param name="fileDataLine">文件数据行</param>
        /// <returns></returns>
        private string GetDeleteQuotaDataLine(string fileDataLine)
        {
            return fileDataLine.Replace("\"\"", "\"");
        }

        /// <summary>
        /// 判断字符串是否包含奇数个引号
        /// </summary>
        /// <param name="dataLine">数据行</param>
        /// <returns>为奇数时，返回为真；否则返回为假</returns>
        private bool IfOddQuota(string dataLine)
        {
            int quotaCount;
            bool oddQuota;

            quotaCount = 0;
            for (int i = 0; i < dataLine.Length; i++)
            {
                if (dataLine[i] == '\"')
                {
                    quotaCount++;
                }
            }

            oddQuota = false;
            if (quotaCount % 2 == 1)
            {
                oddQuota = true;
            }

            return oddQuota;
        }

        /// <summary>
        /// 判断是否以奇数个引号开始

        /// </summary>
        /// <param name="dataCell"></param>
        /// <returns></returns>
        private bool IfOddStartQuota(string dataCell)
        {
            int quotaCount;
            bool oddQuota;

            quotaCount = 0;
            for (int i = 0; i < dataCell.Length; i++)
            {
                if (dataCell[i] == '\"')
                {
                    quotaCount++;
                }
                else
                {
                    break;
                }
            }

            oddQuota = false;
            if (quotaCount % 2 == 1)
            {
                oddQuota = true;
            }

            return oddQuota;
        }

        /// <summary>
        /// 判断是否以奇数个引号结尾
        /// </summary>
        /// <param name="dataCell"></param>
        /// <returns></returns>
        private bool IfOddEndQuota(string dataCell)
        {
            int quotaCount;
            bool oddQuota;

            quotaCount = 0;
            for (int i = dataCell.Length - 1; i >= 0; i--)
            {
                if (dataCell[i] == '\"')
                {
                    quotaCount++;
                }
                else
                {
                    break;
                }
            }

            oddQuota = false;
            if (quotaCount % 2 == 1)
            {
                oddQuota = true;
            }

            return oddQuota;
        }

        /// <summary>
        /// 加入新的数据行
        /// </summary>
        /// <param name="newDataLine">新的数据行</param>
        private void AddNewDataLine(string newDataLine)
        {
            int Column = 0;
            DataRow Row = csvDT.NewRow();
            ArrayList colAL = new ArrayList();
            string[] dataArray = newDataLine.Split(',');
            bool oddStartQuota; //是否以奇数个引号开始
            string cellData;

            oddStartQuota = false;
            cellData = "";

            for (int j = 0; j < dataArray.Length; j++)
            {
                if (IsFirst)
                {
                    DataColumn dc = new DataColumn(dataArray[j]);
                    csvDT.Columns.Add(dc);
                }
                else
                {
                    if (oddStartQuota)
                    {
                        //因为前面用逗号分割,所以要加上逗号
                        cellData += "," + dataArray[j];
                        //是否以奇数个引号结尾
                        if (IfOddEndQuota(dataArray[j]))
                        {

                            Row[Column] = GetHandleData(cellData);
                            Column++;
                            oddStartQuota = false;
                            continue;
                        }
                    }
                    else
                    {
                        //是否以奇数个引号开始
                        if (IfOddStartQuota(dataArray[j]))
                        {
                            //是否以奇数个引号结尾,不能是一个双引号,并且不是奇数个引号

                            if (IfOddEndQuota(dataArray[j]) && dataArray[j].Length > 2 && !IfOddQuota(dataArray[j]))
                            {

                                Row[Column] = GetHandleData(dataArray[j]);
                                Column++;
                                oddStartQuota = false;
                                continue;
                            }
                            else
                            {
                                oddStartQuota = true;
                                cellData = dataArray[j];
                                continue;
                            }
                        }
                        else
                        {
                            Row[Column] = GetHandleData(dataArray[j]);
                            Column++;
                        }
                    }

                }
            }

            if (!IsFirst)
            {
                this.csvDT.Rows.Add(Row);
            }
            IsFirst = false;
            if (oddStartQuota)
            {
                throw new Exception("数据格式有问题");
            }

        }


        /// <summary>
        /// 去掉格子的首尾引号，把双引号变成单引号

        /// </summary>
        /// <param name="fileCellData"></param>
        /// <returns></returns>
        private string GetHandleData(string fileCellData)
        {
            if (fileCellData == "")
            {
                return "";
            }
            if (IfOddStartQuota(fileCellData))
            {
                if (IfOddEndQuota(fileCellData))
                {
                    return fileCellData.Substring(1, fileCellData.Length - 2).Replace("\"\"", "\""); //去掉首尾引号，然后把双引号变成单引号
                }
                else
                {
                    throw new Exception("数据引号无法匹配" + fileCellData);
                }
            }
            else
            {
                //考虑形如"" """" """""" 
                if (fileCellData.Length > 2 && fileCellData[0] == '\"')
                {
                    fileCellData = fileCellData.Substring(1, fileCellData.Length - 2).Replace("\"\"", "\""); //去掉首尾引号，然后把双引号变成单引号
                }
            }

            return fileCellData;
        }
    }
}
